/*
题目: 打开转盘锁
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字： '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转：例如把 '9' 变为 '0'，'0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。

锁的初始数字为 '0000' ，一个代表四个拨轮的数字的字符串。

列表 deadends 包含了一组死亡数字，一旦拨轮的数字和列表里的任何一个元素相同，这个锁将会被永久锁定，无法再被旋转。

字符串 target 代表可以解锁的数字，你需要给出解锁需要的最小旋转次数，如果无论如何不能解锁，返回 -1 。

https://leetcode.cn/problems/open-the-lock
 */
public class OpenLock {
    public int openLock(String[] deadends, String target) {
        Set<String> death = new HashSet<> ();
        for (String s :deadends)
            death.add(s);

        if (death.contains("0000"))
            return -1;
        if (target.equals("0000"))  return 0;

        // 防止多次访问
        Set<String> visited = new HashSet<> ();

        Queue<String> queue = new LinkedList<> ();
        queue.offer("0000");
        visited.add("0000");

        int step = 1;
        while (!queue.isEmpty()) {
            int size = queue.size();

            while (size-- > 0) {
                StringBuilder cur = new StringBuilder(queue.poll());
                // 四个位置的数字, 每次都能向上, 或者向下拨
                for (int i = 0; i < 4; i++) {
                    char temp = cur.charAt(i);
                    char up, down;
                    if (temp == '9')
                        up = '0';
                    else
                        up = (char) (temp + 1);

                    if (temp == '0')
                        down = '9';
                    else
                        down = (char) (temp - 1);

                    cur.setCharAt(i, up);
                    String str = cur.toString();
                    if (str.equals(target)) return step;  // 找到答案
                    // 不在死锁名单 并且 没有访问过
                    if (!death.contains(str) && !visited.contains(str)) {
                        queue.offer(str);
                    }
                    visited.add(str);

                    cur.setCharAt(i, down);
                    str = cur.toString();
                    if (str.equals(target)) return step;
                    if (!death.contains(str) && !visited.contains(str)) {
                        queue.offer(str);
                    }
                    visited.add(str);
                    cur.setCharAt(i, temp);
                }
            }

            step ++;
        }
        return -1;
    }
}
